using System;
using System.Threading;
using Rhizome.Contract;

namespace Rhizome.Model
{
    public partial class ActiveObjectHost
    {
        #region Nested type: Activator

        private class Activator
        {
            private readonly ActiveObjectHost _host;
            private bool _terminate;
            private Thread _thread;
            //private SynchronizationContext _synchronizationContext;

            public Activator(ActiveObjectHost host)
            {
                _host = host;
            }

            public bool CheckAccess
            {
                get { return Thread.CurrentThread == _thread; }
            }

            public event EventHandler StepFailed;

            private void Execute()
            {
                //SynchronizationContext.SetSynchronizationContext(_synchronizationContext); // todo: only in full trust

                while (!_terminate)
                {
                    if (_host._dispatcher.Count != 0)
                    {
                        var obj = _host._dispatcher.GetFirst();
                        while (obj != null)
                        {
                            ExecuteStep(obj);
                            obj = _host._dispatcher.GetNext();
                        }
                    }
                    else
                        _host._dispatcher.Wait();

                    for (var obj = _host._timer.GetReady(); obj != null; obj = _host._timer.GetReady())
                        /*if (obj.Active) */
                        _host._dispatcher.WakeUpByTimer(obj);
                }
            }

            private void ExecuteStep(IActiveObject activeObject)
            {
                var handler = StepFailed;
                if (handler == null)
                {
                    activeObject.Step();
                    return;
                }

                try
                {
                    activeObject.Step();
                }
                catch (Exception e)
                {
                    activeObject.Exception = e;
                    _host._dispatcher.Remove(activeObject);

                    handler(activeObject, EventArgs.Empty);
                }
            }

            public void Start(
                string threadName
                , bool background
#if !SILVERLIGHT && !WINDOWS_PHONE
                , ThreadPriority priority = ThreadPriority.Normal
                , ApartmentState apartmentState = ApartmentState.MTA
#endif
                )
            {
                lock (this)
                {
                    if (_thread != null) return;

                    _terminate = false;
                    //_synchronizationContext = SynchronizationContext.Current;
                    _thread =
                        new Thread(Execute)
                            {
#if !SILVERLIGHT && !WINDOWS_PHONE
                                Priority = priority,
#endif
                                Name = threadName ?? string.Format("{0}_{1}", GetType().Name, Guid.NewGuid()),
                                IsBackground = background
                            };
#if !SILVERLIGHT && !WINDOWS_PHONE
                    _thread.TrySetApartmentState(apartmentState);
#endif
                    _thread.Start();
                }
            }

            public void Stop()
            {
                lock (this)
                {
                    if (_thread == null) return;

                    _terminate = true;
                    _host._dispatcher.Pulse();
                    _thread.Join();
                    _thread = null;
                }
            }
        }

        #endregion
    }
}